from system.eventServer import eventServer
from system.fsm import Fsm
from system.vec2 import Vec2
from system.colors import colors
from system.timer import Timer

import math
import random
import yaml

class Unit(Fsm):
    def __init__(self, parent, type = None, name = None):
        Fsm.__init__(self, parent, ["update"], type, name)
        
        self.shudai = self.search("type","shudai.shudai")
        self.player = self.search("name","player")
        
        self.defaultVar("gfx","unit")
        self.defaultVar("renderOp2d","sceneNode")
        self.defaultVar("pos",(0,0))
        self.defaultVar("scale",(100,100))
        self.defaultVar("rot",0)
        
        self.defaultVar("moveSpeed",100)
        self.defaultVar("turnSpeed",2)
        
        self.defaultVar("distThreshold",25)
        self.defaultVar("turnThreshold",0.1)
        self.defaultVar("stopAtTarget",True)
        
        self.defaultVar("sight",500)
        self.defaultVar("maxHp",50)
        
        self.defaultVar("buildUnits",[])
        
        self.defaultVar("weapons",{})
        self.defaultVar("colorWeapons",True)
        
        self.defaultVar("side","unassigned")
        
        self.setVar("dead",False)
        
        self.makeBars()
        
        eventServer.register("unitMoveTo",self.eventMoveTo)
        eventServer.register("unitTarget",self.eventTarget)
        eventServer.register("unitAttack",self.eventAttack)
        eventServer.register("unitWeaponAttack",self.eventWeaponAttack)
        eventServer.register("unitFollow",self.eventFollow)
        eventServer.register("unitStop",self.eventStop)
        eventServer.register("unitSetSide",self.eventSetSide)
        eventServer.register("unitDamage",self.unitDamage)
        eventServer.register("unitStopWeapons",self.unitStopWeapons)
        eventServer.register("unitStopWeapon",self.unitStopWeapon)
        eventServer.register("unitBuild",self.unitBuild)
        
    def makeBars(self):
        self.healthBar = self.addChild("healthBar")
        self.healthBar.setVar("renderOp2d","rect")
        self.healthBar.setVar("pos",(-0.5,-0.6))
        self.healthBar.setVar("scale",(1,0.1))
        self.healthBar.setVar("color",(1,1,1))
        self.healthBar.setVar("filled",False)
        self.healthBar.setVar("shown",False)
        
        self.healthFill = self.healthBar.addChild("healthFill")
        self.healthFill.setVar("renderOp2d","rect")
        
        self.buildBar = self.addChild("buildBar")
        self.buildBar.setVar("renderOp2d","rect")
        self.buildBar.setVar("pos",(-0.5,0.5))
        self.buildBar.setVar("scale",(1,0.1))
        self.buildBar.setVar("color",(1,1,1))
        self.buildBar.setVar("filled",False)
        self.buildBar.setVar("shown",False)
        
        self.buildFill = self.buildBar.addChild("buildFill")
        self.buildFill.setVar("renderOp2d","rect")
        
    def unitBuild(self, cmd, args):
        if args['path'] == self.getVar("path") and args['type'] in self.getVar("buildUnits") and self.getVar("state") != "building":
            self.setVar("buildType",args["type"])
            self.transition("building")
            
    def start_building(self, cmd, args):
        self.buildBar.setVar("shown",True)
        self.buildFill.setVar("scale",(0,1))
        self.buildFill.setVar("color",(0,0,0))
        
        try:
            data = yaml.load(open("dat/objects/%s.yml" % self.getVar("buildType")))
        except:
            data = None
            
        buildTime = 5
        buildCost = {}
        costOk = True
            
        if data and data.has_key('vars'):
            if data['vars'].has_key('buildTime'):
                buildTime = data['vars']['buildTime']
                
            for resource in self.shudai.getVar("resources"):
                print "looking for cost", resource
                costName = 'cost_%s' % resource
                if data['vars'].has_key(costName):
                    cost = data['vars'][costName]
                else:
                    cost = 0
                print "cost", cost
                buildCost[resource] = cost
                    
                if self.player.getVar(resource) < cost:
                    costOk = False
                    
        if costOk:
            for resource, cost in buildCost.iteritems():
                print "deduct cost", resource, self.player.getVar(resource)
                self.player.setVar(resource,self.player.getVar(resource)-cost)
                print self.player.getVar(resource)
                eventServer.pull("changeResource",{"resource":resource})
            self.buildTimer = Timer(buildTime)
            
    def update_building(self, cmd, args):
        if self.buildTimer.fire():
            self.transition("idle")
            
        else:
            time = self.buildTimer.getTime() / float(self.buildTimer.timeout)
            self.buildFill.setVar("scale",(time,1))
            self.buildFill.setVar("color",(time,time,time))
            
    def end_building(self, cmd, args):
        r = self.getVar("boundingRadius") * self.getVar("scale")[0] * 2
        th = random.random() * math.pi * 2
        x,y = self.getVar("pos")
        pos = (r * math.cos(th) + x,r * math.sin(th) + y)
        eventServer.netPull('createUnit',{'type':self.getVar("buildType"),'pos':pos,'side':self.getVar("side")})
        self.buildBar.setVar("shown",False)
        
    def unitStopWeapons(self, cmd, args):
        if args['path'] == self.getVar("path"):
            for weapon in self.weapons.children.itervalues():
                weapon.transition("idle")
                
    def unitStopWeapon(self, cmd, args):
        if args['path'] == self.getVar("path") and self.weapons.children.has_key(args['weapon']):
            self.weapons.children[args['weapon']].transition('idle')
        
    def unitDamage(self, cmd, args):
        if args['path'] == self.getVar("path") and not self.getVar('dead'):
            self.setVar("hp",self.getVar("hp") - args['damage'])
            if self.getVar("hp") < 0:
                self.setVar("dead",True)
                self.parent.removeChild(self.getVar("name"))
                
            self.healthBar.setVar("shown",True)
            rat = float(self.getVar("hp")) / float(self.getVar("maxHp"))
            self.healthFill.setVar("color",(1.0 - rat, rat, 0))
            self.healthFill.setVar("scale",(rat,1))
        
    def eventSetSide(self, cmd, args):
        self.setSide(args['side'])
        
    def setSide(self, side):
        self.setVar("side",side)
        if colors.has_key(side):
            color = colors[side]
        else:
            color = colors['grey']
            
        for child in self.gfx.children.itervalues():
            child.setVar("color",color)
            
        if self.getVar("colorWeapons"):
            for weapon in self.weapons.children.itervalues():
                for child in weapon.gfx.children.itervalues():
                    child.setVar("color",color)
        
    def eventMoveTo(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.moveTo(args['pos'])
        
    def moveTo(self, point):
        self.setVar("moveToPos",point)
        self.transition("moveTo")
        
    def eventTarget(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.target(args['target'])
        
    def target(self, target):
        for weapon in self.weapons.children.itervalues():
            weapon.target(target)
            
    def eventAttack(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.attack(args['target'])
            
    def attack(self, target):
        for weapon in self.weapons.children.itervalues():
            weapon.attack(target)
            
    def eventWeaponAttack(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.weaponAttack(args['weapon'],args['target'])
        
    def weaponAttack(self, weapon, target):
        if self.weapons.has_key(weapon):
            self.weapons[weapon].attack(target)
            
    def eventFollow(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.follow(args['target'])
        
    def follow(self, target):
        obj = self.getObject(target)
        if obj and obj.hasVar("pos"):
            self.followObj = obj
            self.transition('follow')
            
    def eventStop(self, cmd, args):
        if args['path'] == self.getVar("path"):
            self.stop()
        
    def stop(self):
        self.transition("idle")
        
    def start_follow(self, cmd, args):
        self.setVar("stopAtTarget",False)
        
    def stop_follow(self, cmd, args):
        self.setVar("stopAtTarget",True)
        
    def update_follow(self, cmd, args):
        if self.followObj:
            if self.followObj.getVar("dead"):
                self.followObj = None
                return
            
            myPos = Vec2(self.getVar("pos"))
            followPos = Vec2(self.followObj.getVar("pos"))
            direction = (followPos - myPos).norm()
            mySize = self.getVar("boundingRadius") * self.getVar("scale")[0]
            if self.followObj.hasVar("boundingRadius") and self.followObj.hasVar("scale"):
                followSize = self.followObj.getVar("boundingRadius") * self.followObj.getVar("scale")[0]
            else:
                followSize = 0
            self.setVar("moveToPos",(followPos - direction * (mySize + followSize)).toTuple())
            self.update_moveTo(cmd,args)
            
    # HACK -dtime
    # This function requires some cleaning up...
    def update_moveTo(self, cmd, args):
        dtime = -abs(args['dtime'])
        
        pos = Vec2(self.getVar("pos"))
        dest = Vec2(self.getVar("moveToPos"))
        diff = pos - dest
        diffDist = diff.length()
        
        rot = self.getVar("rot") + math.pi / 2
        destRot = diff.angle()
        diffRot = destRot - rot
        while diffRot < math.pi:
            diffRot += math.pi * 2
        while diffRot > math.pi:
            diffRot -= math.pi * 2
            
        forward = Vec2(math.cos(rot),math.sin(rot))
        
        if diffDist < self.getVar("distThreshold"):
            if self.getVar("stopAtTarget"):
                self.transition("idle")
        else:
            turnGain = 1 - abs(diffRot) / math.pi
            newPos = pos + forward * self.getVar("moveSpeed") * dtime * turnGain
            self.setVar("pos",newPos.toTuple())
            
        if diffRot > self.getVar("turnThreshold"):
            self.setVar("rot",self.getVar("rot") - self.getVar("turnSpeed") * dtime)
        elif diffRot < -self.getVar("turnThreshold"):
            self.setVar("rot",self.getVar("rot") + self.getVar("turnSpeed") * dtime)
        
    def init(self):
        self.setVar("hp",self.getVar("maxHp"))
        
        self.gfx = self.addChild(self.getVar("gfx"),"gfx")
        self.gfx.setVar("static",True)
        
        points = self.flattenVars("points")
        if points:
            sumx = sumy = count = 0
            for px, py in points:
                sumx += px
                sumy += py
                count += 1
                
            cx = sumx / float(count)
            cy = sumy / float(count)
            
        else:
            cx = cy = 0.5
            
        maxDist = None
        for px, py in points:
            dx = px - cx
            dy = py - cy
            dist = math.sqrt(dx * dx + dy * dy)
            if not maxDist or dist > maxDist:
                maxDist = dist
        if not maxDist:
            maxDist = 1
            
        self.setVar("center",(cx,cy))
        self.setVar("boundingRadius",maxDist)
        self.gfx.setVar("pos",(-cx,-cy))
        
        self.initWeapons()
        
    def initWeapons(self):
        self.weapons = self.addChild("weapons")
        
        for name, data in self.getVar("weapons").iteritems():
            if data.has_key('type'):
                type = data['type']
            else:
                type = name.split('_')[0]
                
            if data.has_key('hardpoint'):
                hardpoint = data['hardpoint']
            else:
                hardpoint = 0
                
            hardpoint = Vec2(self.gfx.getVar("hardpoints")[hardpoint])
            
            center = Vec2(self.getVar("center"))
            
            #pos = hardpoint + center
            pos = hardpoint - Vec2(0.5,0.5)
            
            self.weapons.addChildData(data)
            weapon = self.weapons.addChild("shudai.weapon",name)
            weapon.load(type)
            weapon.setVar("pos",pos.toTuple())
            #weapon.setVar("pos",(0,0))
            weapon.init()
